#include <netinet/in.h>
#include <stdlib.h>

#include <CUnit/CUnit.h>

#include "../file_mapper.h"
#include "../pcm.h"

/* sample data taken from here: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
static unsigned char pcm_little_endian_wav_example[72] = {
0x52, 0x49, 0x46, 0x46, 0x40, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 
0x22, 0x56, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x24, 0x17, 0x1e, 0xf3, 0x3c, 0x13, 0x3c, 0x14, 0x16, 0xf9, 0x18, 0xf9, 0x34, 0xe7, 0x23, 0xa6, 0x3c, 0xf2, 0x24, 0xf2, 0x11, 0xce, 0x1a, 0x0d
};

static unsigned char pcm_big_endian_wav_example[72] = {
0x52, 0x49, 0x46, 0x58, 0x00, 0x00, 0x00, 0x40, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x02,
0x00, 0x00, 0x56, 0x22, 0x00, 0x01, 0x58, 0x88, 0x00, 0x04, 0x00, 0x10, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00,
0x17, 0x24, 0xf3, 0x1e, 0x13, 0x3c, 0x14, 0x3c, 0xf9, 0x16, 0xf9, 0x18, 0xe7, 0x34, 0xa6, 0x23, 0xf2, 0x3c, 0xf2, 0x24, 0xce, 0x11, 0x0d, 0x1a
};

static struct file_mapper *
create_test_file_mapper()
{
	struct mediafs_decoder_stat decoder_stat;
	struct pcm_encoder_params encoder_params;

	struct mediafs_decoder *decoder;
	struct mediafs_encoder *encoder;
	struct mediafs_decoder_ctx *decoder_ctx;
	struct mediafs_encoder_ctx *encoder_ctx;
	struct file_mapper *result;
	int res;

	decoder = get_mediafs_pcm_decoder();
	decoder_ctx = mediafs_decoder_open(decoder, pcm_big_endian_wav_example, sizeof(pcm_little_endian_wav_example));

	memset(&decoder_stat, 0, sizeof(decoder_stat));
	res = mediafs_decoder_get_stat(decoder_ctx, &decoder_stat);
	CU_ASSERT_EQUAL(res, 0);

	encoder = get_mediafs_pcm_encoder();
	fill_mediafs_pcm_encoder_params(&encoder_params, &decoder_stat);
	encoder_ctx = mediafs_encoder_open(encoder, (struct mediafs_encoder_params *)&encoder_params);

	result = file_mapper_open(decoder_ctx, encoder_ctx);
	CU_ASSERT_PTR_NOT_NULL(result);
	return result;
}

static void
destroy_test_file_mapper(struct file_mapper *fmapper)
{
	mediafs_decoder_close(fmapper->decoder_ctx);
	mediafs_encoder_close(fmapper->encoder_ctx);
	file_mapper_close(fmapper);
}

static void
test_wav_big_to_little_endian_size_is_mapped_correctly()
{
	struct file_mapper *fmapper;

	fmapper = create_test_file_mapper();
	CU_ASSERT_EQUAL(file_mapper_get_mapped_size(fmapper), 72);
	destroy_test_file_mapper(fmapper);
}

static void
test_wav_big_to_little_endian_header_is_mapped_correctly()
{
	char buffer[44];
	struct file_mapper *fmapper;
	int res;
	
	fmapper = create_test_file_mapper();
	res = file_mapper_get_mapped_bytes(fmapper, 0, sizeof(buffer), buffer);
	CU_ASSERT_EQUAL(res, 0);
	CU_ASSERT_EQUAL(memcmp(buffer, pcm_little_endian_wav_example, sizeof(buffer)), 0);
	destroy_test_file_mapper(fmapper);
}

static void
test_wav_big_to_little_endian_audio_data_are_mapped_correctly()
{
	char buffer[28];
	struct file_mapper *fmapper;
	int res;

	fmapper = create_test_file_mapper();
	res = file_mapper_get_mapped_bytes(fmapper, 44, sizeof(buffer), buffer);
	CU_ASSERT_EQUAL(res, 0);
	CU_ASSERT_EQUAL(memcmp(buffer, pcm_little_endian_wav_example + 44, sizeof(buffer)), 0);
	destroy_test_file_mapper(fmapper);
}

static void
test_wav_big_to_little_endian_is_mapped_correctly_as_a_whole()
{
	char buffer[72];
	struct file_mapper *fmapper;
	int res;

	fmapper = create_test_file_mapper();
	res = file_mapper_get_mapped_bytes(fmapper, 0, sizeof(buffer), buffer);
	CU_ASSERT_EQUAL(res, 0);
	CU_ASSERT_EQUAL(memcmp(buffer, pcm_little_endian_wav_example, sizeof(buffer)), 0);
	/* size_t i;
	for (i = 0; i < sizeof(buffer); ++i) {
		printf("0x%02x ", (unsigned char)buffer[i]);
	}
	printf("\n");*/


	destroy_test_file_mapper(fmapper);
}

static void
test_wav_big_to_little_endian_is_mapped_correctly_when_read_in_tuplets()
{
	unsigned char buffer[2];
	struct file_mapper *fmapper;
	int res, i;

	fmapper = create_test_file_mapper();
	for (i = 0; i < 72 / 2; ++i) {
		res = file_mapper_get_mapped_bytes(fmapper, i * 2, sizeof(buffer), buffer);
		CU_ASSERT_EQUAL(res, 0);
		CU_ASSERT_EQUAL(memcmp(buffer, pcm_little_endian_wav_example + i * 2, sizeof(buffer)), 0);
		//printf("0x%02x 0x%02x\n", buffer[0], buffer[1]);
	}
	destroy_test_file_mapper(fmapper);
}

static void
test_wav_big_to_little_endian_is_mapped_correctly_when_read_in_pentuplets()
{
	unsigned char buffer[5];
	struct file_mapper *fmapper;
	int res, i;

	fmapper = create_test_file_mapper();
	for (i = 0; i < 72 / 5; ++i) {
		res = file_mapper_get_mapped_bytes(fmapper, i * 5, sizeof(buffer), buffer);
		CU_ASSERT_EQUAL(res, 0);
		CU_ASSERT_EQUAL(memcmp(buffer, pcm_little_endian_wav_example + i * 5, sizeof(buffer)), 0);
		//printf("0x%02x 0x%02x\n", buffer[0], buffer[1]);
	}
	destroy_test_file_mapper(fmapper);

}

void
add_file_mapper_test_suite()
{	
	CU_pSuite result;

	result = CU_add_suite("file_mapper", NULL, NULL);

	CU_ADD_TEST(result, test_wav_big_to_little_endian_size_is_mapped_correctly);
	CU_ADD_TEST(result, test_wav_big_to_little_endian_header_is_mapped_correctly);
	CU_ADD_TEST(result, test_wav_big_to_little_endian_audio_data_are_mapped_correctly);
	CU_ADD_TEST(result, test_wav_big_to_little_endian_is_mapped_correctly_as_a_whole);
	CU_ADD_TEST(result, test_wav_big_to_little_endian_is_mapped_correctly_when_read_in_tuplets);
	CU_ADD_TEST(result, test_wav_big_to_little_endian_is_mapped_correctly_when_read_in_pentuplets);
}

